/******************************************************************************* * Copyright (c) 2000, 2017 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * *******************************************************************************/ package org.eclipse.dltk.internal.ui.refactoring.reorg; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.dltk.core.DLTKCore; import org.eclipse.dltk.core.IModelElement; import org.eclipse.dltk.core.IProjectFragment; import org.eclipse.dltk.core.IScriptProject; import org.eclipse.dltk.core.ModelException; import org.eclipse.dltk.internal.core.ExternalProjectFragment; import org.eclipse.dltk.internal.core.ExternalScriptFolder; import org.eclipse.dltk.internal.core.ExternalSourceModule; import org.eclipse.dltk.internal.corext.refactoring.reorg.ModelElementTransfer; import org.eclipse.dltk.internal.corext.refactoring.reorg.ParentChecker; import org.eclipse.dltk.internal.corext.refactoring.reorg.ReorgUtils; import org.eclipse.dltk.internal.ui.refactoring.RefactoringMessages; import org.eclipse.dltk.internal.ui.workingsets.WorkingSetIDs; import org.eclipse.dltk.ui.DLTKUIPlugin; import org.eclipse.dltk.ui.actions.SelectionDispatchAction; import org.eclipse.dltk.ui.util.ExceptionHandler; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.swt.dnd.Clipboard; import org.eclipse.swt.dnd.FileTransfer; import org.eclipse.swt.dnd.Transfer; import org.eclipse.swt.dnd.TransferData; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.ISharedImages; import org.eclipse.ui.IWorkbenchSite; import org.eclipse.ui.IWorkingSet; import org.eclipse.ui.actions.CopyFilesAndFoldersOperation; import org.eclipse.ui.actions.CopyProjectOperation; import org.eclipse.ui.part.ResourceTransfer; public class PasteAction extends SelectionDispatchAction { private final Clipboard fClipboard; public PasteAction(IWorkbenchSite site, Clipboard clipboard) { super(site); Assert.isNotNull(clipboard); fClipboard = clipboard; setText(ReorgMessages.PasteAction_4); setDescription(ReorgMessages.PasteAction_5); ISharedImages workbenchImages = DLTKUIPlugin.getDefault().getWorkbench() .getSharedImages(); setDisabledImageDescriptor(workbenchImages .getImageDescriptor(ISharedImages.IMG_TOOL_PASTE_DISABLED)); setImageDescriptor(workbenchImages .getImageDescriptor(ISharedImages.IMG_TOOL_PASTE)); setHoverImageDescriptor(workbenchImages .getImageDescriptor(ISharedImages.IMG_TOOL_PASTE)); if (DLTKCore.DEBUG) { System.err.println("Add help support here..."); //$NON-NLS-1$ } // PlatformUI.getWorkbench().getHelpSystem().setHelp(this, // IScriptHelpContextIds.PASTE_ACTION); } @Override public void selectionChanged(IStructuredSelection selection) { // Moved condition checking to run (see // http://bugs.eclipse.org/bugs/show_bug.cgi?id=78450) } private Paster[] createEnabledPasters(TransferData[] availableDataTypes) throws ModelException { Paster paster; Shell shell = getShell(); List<Paster> result = new ArrayList<>(2); paster = new ProjectPaster(shell, fClipboard); if (paster.canEnable(availableDataTypes)) result.add(paster); paster = new ModelElementAndResourcePaster(shell, fClipboard); if (paster.canEnable(availableDataTypes)) result.add(paster); paster = new FilePaster(shell, fClipboard); if (paster.canEnable(availableDataTypes)) result.add(paster); paster = new WorkingSetPaster(shell, fClipboard); if (paster.canEnable(availableDataTypes)) result.add(paster); return result.toArray(new Paster[result.size()]); } private static Object getContents(final Clipboard clipboard, final Transfer transfer, Shell shell) { // see bug 33028 for explanation why we need this final Object[] result = new Object[1]; shell.getDisplay() .syncExec(() -> result[0] = clipboard.getContents(transfer)); return result[0]; } private static boolean isAvailable(Transfer transfer, TransferData[] availableDataTypes) { for (int i = 0; i < availableDataTypes.length; i++) { if (transfer.isSupportedType(availableDataTypes[i])) return true; } return false; } @Override public void run(IStructuredSelection selection) { try { TransferData[] availableTypes = fClipboard.getAvailableTypes(); List elements = selection.toList(); IResource[] resources = ReorgUtils.getResources(elements); IModelElement[] modelElements = ReorgUtils .getModelElements(elements); IWorkingSet[] workingSets = ReorgUtils.getWorkingSets(elements); Paster[] pasters = createEnabledPasters(availableTypes); for (int i = 0; i < pasters.length; i++) { if (pasters[i].canPasteOn(modelElements, resources, workingSets)) { pasters[i].paste(modelElements, resources, workingSets, availableTypes); return;// one is enough } } MessageDialog.openError(DLTKUIPlugin.getActiveWorkbenchShell(), RefactoringMessages.OpenRefactoringWizardAction_refactoring, RefactoringMessages.OpenRefactoringWizardAction_disabled); } catch (ModelException e) { ExceptionHandler.handle(e, RefactoringMessages.OpenRefactoringWizardAction_refactoring, RefactoringMessages.OpenRefactoringWizardAction_exception); } catch (InvocationTargetException e) { ExceptionHandler.handle(e, RefactoringMessages.OpenRefactoringWizardAction_refactoring, RefactoringMessages.OpenRefactoringWizardAction_exception); } catch (InterruptedException e) { // OK } } private abstract static class Paster { private final Shell fShell; private final Clipboard fClipboard2; protected Paster(Shell shell, Clipboard clipboard) { fShell = shell; fClipboard2 = clipboard; } protected final Shell getShell() { return fShell; } protected final Clipboard getClipboard() { return fClipboard2; } protected final IResource[] getClipboardResources( TransferData[] availableDataTypes) { Transfer transfer = ResourceTransfer.getInstance(); if (isAvailable(transfer, availableDataTypes)) { return (IResource[]) getContents(fClipboard2, transfer, getShell()); } return null; } protected final IModelElement[] getClipboardScriptElements( TransferData[] availableDataTypes) { Transfer transfer = ModelElementTransfer.getInstance(); if (isAvailable(transfer, availableDataTypes)) { return (IModelElement[]) getContents(fClipboard2, transfer, getShell()); } return null; } public abstract void paste(IModelElement[] selectedScriptElements, IResource[] selectedResources, IWorkingSet[] selectedWorkingSets, TransferData[] availableTypes) throws ModelException, InterruptedException, InvocationTargetException; public abstract boolean canEnable(TransferData[] availableTypes) throws ModelException; public abstract boolean canPasteOn( IModelElement[] selectedScriptElements, IResource[] selectedResources, IWorkingSet[] selectedWorkingSets) throws ModelException; } private static class WorkingSetPaster extends Paster { protected WorkingSetPaster(Shell shell, Clipboard clipboard) { super(shell, clipboard); } @Override public void paste(IModelElement[] selectedScriptElements, IResource[] selectedResources, IWorkingSet[] selectedWorkingSets, TransferData[] availableTypes) throws ModelException, InterruptedException, InvocationTargetException { IWorkingSet workingSet = selectedWorkingSets[0]; Set<IAdaptable> elements = new HashSet<>( Arrays.asList(workingSet.getElements())); IModelElement[] modelElements = getClipboardScriptElements( availableTypes); if (modelElements != null) { for (int i = 0; i < modelElements.length; i++) { if (!ReorgUtils.containsElementOrParent(elements, modelElements[i])) elements.add(modelElements[i]); } } IResource[] resources = getClipboardResources(availableTypes); if (resources != null) { List realScriptElements = new ArrayList(); List realResource = new ArrayList(); ReorgUtils.splitIntoModelElementsAndResources(resources, realScriptElements, realResource); for (Iterator iter = realScriptElements.iterator(); iter .hasNext();) { IModelElement element = (IModelElement) iter.next(); if (!ReorgUtils.containsElementOrParent(elements, element)) elements.add(element); } for (Iterator iter = realResource.iterator(); iter.hasNext();) { IResource element = (IResource) iter.next(); if (!ReorgUtils.containsElementOrParent(elements, element)) elements.add(element); } } workingSet.setElements( elements.toArray(new IAdaptable[elements.size()])); } @Override public boolean canEnable(TransferData[] availableTypes) throws ModelException { return isAvailable(ResourceTransfer.getInstance(), availableTypes) || isAvailable(ModelElementTransfer.getInstance(), availableTypes); } @Override public boolean canPasteOn(IModelElement[] selectedScriptElements, IResource[] selectedResources, IWorkingSet[] selectedWorkingSets) throws ModelException { if (selectedResources.length != 0 || selectedScriptElements.length != 0 || selectedWorkingSets.length != 1) return false; IWorkingSet ws = selectedWorkingSets[0]; return !WorkingSetIDs.OTHERS.equals(ws.getId()); } } private static class ProjectPaster extends Paster { protected ProjectPaster(Shell shell, Clipboard clipboard) { super(shell, clipboard); } @Override public boolean canEnable(TransferData[] availableDataTypes) { boolean resourceTransfer = isAvailable( ResourceTransfer.getInstance(), availableDataTypes); boolean modelElementTransfer = isAvailable( ModelElementTransfer.getInstance(), availableDataTypes); if (!modelElementTransfer) return canPasteSimpleProjects(availableDataTypes); if (!resourceTransfer) return canPasteScriptProjects(availableDataTypes); return canPasteScriptProjects(availableDataTypes) && canPasteSimpleProjects(availableDataTypes); } @Override public void paste(IModelElement[] modelElements, IResource[] resources, IWorkingSet[] selectedWorkingSets, TransferData[] availableTypes) { pasteProjects(availableTypes); } private void pasteProjects(TransferData[] availableTypes) { pasteProjects(getProjectsToPaste(availableTypes)); } private void pasteProjects(IProject[] projects) { Shell shell = getShell(); for (int i = 0; i < projects.length; i++) { new CopyProjectOperation(shell).copyProject(projects[i]); } } private IProject[] getProjectsToPaste(TransferData[] availableTypes) { IResource[] resources = getClipboardResources(availableTypes); IModelElement[] modelElements = getClipboardScriptElements( availableTypes); Set result = new HashSet(); if (resources != null) result.addAll(Arrays.asList(resources)); if (modelElements != null) result.addAll(Arrays.asList(ReorgUtils .getNotNulls(ReorgUtils.getResources(modelElements)))); Assert.isTrue(result.size() > 0); return (IProject[]) result.toArray(new IProject[result.size()]); } @Override public boolean canPasteOn(IModelElement[] modelElements, IResource[] resources, IWorkingSet[] selectedWorkingSets) { return selectedWorkingSets.length == 0; // Can't paste on working // sets here } private boolean canPasteScriptProjects( TransferData[] availableDataTypes) { IModelElement[] modelElements = getClipboardScriptElements( availableDataTypes); return modelElements != null && modelElements.length != 0 && !ReorgUtils.hasElementsNotOfType(modelElements, IModelElement.SCRIPT_PROJECT); } private boolean canPasteSimpleProjects( TransferData[] availableDataTypes) { IResource[] resources = getClipboardResources(availableDataTypes); if (resources == null || resources.length == 0) return false; for (int i = 0; i < resources.length; i++) { if (resources[i].getType() != IResource.PROJECT || !((IProject) resources[i]).isOpen()) return false; } return true; } } private static class FilePaster extends Paster { protected FilePaster(Shell shell, Clipboard clipboard) { super(shell, clipboard); } @Override public void paste(IModelElement[] modelElements, IResource[] resources, IWorkingSet[] selectedWorkingSets, TransferData[] availableTypes) throws ModelException { String[] fileData = getClipboardFiles(availableTypes); if (fileData == null) return; IContainer container = getAsContainer( getTarget(modelElements, resources)); if (container == null) return; new CopyFilesAndFoldersOperation(getShell()).copyFiles(fileData, container); } private Object getTarget(IModelElement[] modelElements, IResource[] resources) { if (modelElements.length + resources.length == 1) { if (modelElements.length == 1) return modelElements[0]; else return resources[0]; } else return getCommonParent(modelElements, resources); } @Override public boolean canPasteOn(IModelElement[] modelElements, IResource[] resources, IWorkingSet[] selectedWorkingSets) throws ModelException { Object target = getTarget(modelElements, resources); return target != null && canPasteFilesOn(getAsContainer(target)) && selectedWorkingSets.length == 0; } @Override public boolean canEnable(TransferData[] availableDataTypes) throws ModelException { return isAvailable(FileTransfer.getInstance(), availableDataTypes); } private boolean canPasteFilesOn(Object target) { boolean isScriptFolder = target instanceof IProjectFragment; boolean isScriptProject = target instanceof IScriptProject; boolean isProjectFragment = target instanceof IProjectFragment; boolean isContainer = target instanceof IContainer; if (target instanceof ExternalProjectFragment || target instanceof ExternalScriptFolder || target instanceof ExternalSourceModule) { return false; } if (!(isScriptFolder || isScriptProject || isProjectFragment || isContainer)) return false; if (isContainer) { return true; } else { IModelElement element = (IModelElement) target; return !element.isReadOnly(); } } private IContainer getAsContainer(Object target) throws ModelException { if (target == null) return null; if (target instanceof IContainer) return (IContainer) target; if (target instanceof IFile) return ((IFile) target).getParent(); return getAsContainer( ((IModelElement) target).getCorrespondingResource()); } private String[] getClipboardFiles(TransferData[] availableDataTypes) { Transfer transfer = FileTransfer.getInstance(); if (isAvailable(transfer, availableDataTypes)) { return (String[]) getContents(getClipboard(), transfer, getShell()); } return null; } private Object getCommonParent(IModelElement[] modelElements, IResource[] resources) { return new ParentChecker(resources, modelElements) .getCommonParent(); } } private static class ModelElementAndResourcePaster extends Paster { protected ModelElementAndResourcePaster(Shell shell, Clipboard clipboard) { super(shell, clipboard); } private TransferData[] fAvailableTypes; @Override public void paste(IModelElement[] modelElements, IResource[] resources, IWorkingSet[] selectedWorkingSets, TransferData[] availableTypes) throws ModelException, InterruptedException, InvocationTargetException { IResource[] clipboardResources = getClipboardResources( availableTypes); if (clipboardResources == null) clipboardResources = new IResource[0]; IModelElement[] clipboardScriptElements = getClipboardScriptElements( availableTypes); if (clipboardScriptElements == null) clipboardScriptElements = new IModelElement[0]; Object destination = getTarget(modelElements, resources); if (destination instanceof IModelElement) { ReorgCopyStarter.create(clipboardScriptElements, clipboardResources, (IModelElement) destination) .run(getShell()); } else if (destination instanceof IResource) { ReorgCopyStarter.create(clipboardScriptElements, clipboardResources, (IResource) destination) .run(getShell()); } } private Object getTarget(IModelElement[] modelElements, IResource[] resources) { if (modelElements.length + resources.length == 1) { if (modelElements.length == 1) return modelElements[0]; else return resources[0]; } else return getCommonParent(modelElements, resources); } private Object getCommonParent(IModelElement[] modelElements, IResource[] resources) { return new ParentChecker(resources, modelElements) .getCommonParent(); } @Override public boolean canPasteOn(IModelElement[] modelElements, IResource[] resources, IWorkingSet[] selectedWorkingSets) throws ModelException { if (selectedWorkingSets.length != 0) return false; IResource[] clipboardResources = getClipboardResources( fAvailableTypes); if (clipboardResources == null) clipboardResources = new IResource[0]; IModelElement[] clipboardScriptElements = getClipboardScriptElements( fAvailableTypes); if (clipboardScriptElements == null) clipboardScriptElements = new IModelElement[0]; Object destination = getTarget(modelElements, resources); if (destination instanceof IModelElement) { return ReorgCopyStarter.create(clipboardScriptElements, clipboardResources, (IModelElement) destination) != null; } if (destination instanceof IResource) { return ReorgCopyStarter.create(clipboardScriptElements, clipboardResources, (IResource) destination) != null; } return false; } @Override public boolean canEnable(TransferData[] availableTypes) { fAvailableTypes = availableTypes; return isAvailable(ModelElementTransfer.getInstance(), availableTypes) || isAvailable(ResourceTransfer.getInstance(), availableTypes); } } }